package com.autonavi.yunda.yunji.core.engine.scriptresolver;

import com.autonavi.yunda.yunji.common.exception.AmapException;
import com.autonavi.yunda.yunji.common.exception.AmapExceptionCode;
import com.autonavi.yunda.yunji.common.exception.AmapPreconditions;
import lombok.Getter;
import org.codehaus.groovy.ast.*;
import org.codehaus.groovy.ast.builder.AstBuilder;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * groovy代码解析
 */
public class CodeVisitor extends ClassCodeVisitorSupport {
    public static final CodeVisitor EMPTY_VISITOR = new CodeVisitor();
    private final List<String> lines;
    @Getter
    private LinkedHashMap<String, String> methods = new LinkedHashMap<>();

    @Getter
    public LinkedHashSet<String> allImports = new LinkedHashSet<>();

    private CodeVisitor() {
        this.lines = List.of();
    }

    private CodeVisitor(String script) {
        this.lines = new BufferedReader(new StringReader(script)).lines().collect(Collectors.toList());
    }

    public static CodeVisitor toCodeVisitor(String script) {
        // 编译脚本源代码
        List<ASTNode> astNodes = new AstBuilder().buildFromString(CompilePhase.CONVERSION, false, script);
        // 获取ClassNode节点
        ClassNode classNode = (ClassNode) astNodes.stream().filter(node -> node instanceof ClassNode).findFirst().orElse(null);
        AmapPreconditions.checkNotNull(classNode);
        CodeVisitor visitor = new CodeVisitor(script);
        visitor.visitClass(classNode);
        return visitor;
    }

    private String readDetail(ASTNode node) {
        int lineNumber = node.getLineNumber();
        int lastLinenumber = node.getLastLineNumber();
        int columnNumber = node.getColumnNumber();
        int lastColumnNumber = node.getLastColumnNumber();
        StringBuilder builder = new StringBuilder();
        for (int i = lineNumber - 1; i < lastLinenumber; i++) {
            if (i == lineNumber - 1) {
                builder.append(lines.get(i).substring(columnNumber - 1));
            } else if (i == lastLinenumber - 1) {
                builder.append(lines.get(i), 0, lastColumnNumber - 1);
            } else {
                builder.append(lines.get(i));
            }
            builder.append("\n");
        }
        return builder.toString();
    }

    @Override
    protected SourceUnit getSourceUnit() {
        return null;
    }

    @Override
    public void visitMethod(MethodNode node) {
        try {
            methods.put(node.getName(), readDetail(node));
        } catch (Exception e) {
            throw new AmapException(AmapExceptionCode.SCRIPT_RESOLVE_ERROR,
                    "visitMethod error lineNumber:" + node.getLineNumber() + ", columNumber:" + node.getColumnNumber(), e);
        }
    }


    @Override
    public void visitImports(ModuleNode node) {
        try {
            node.getImports()
                    .forEach(it -> allImports.add("import " + it.getType().getName()));
            node.getStarImports()
                    .forEach(it -> allImports.add("import " + it.getPackageName() + "*"));
            node.getStaticImports().values()
                    .forEach(it -> allImports.add("import static " + it.getType().getName()));
            node.getStaticStarImports().values()
                    .forEach(it -> allImports.add("import static " + it.getPackageName() + ".*"));
        } catch (Exception e) {
            throw new AmapException(AmapExceptionCode.SCRIPT_RESOLVE_ERROR,
                    "visitImports error lineNumber:" + node.getLineNumber() + ", columNumber:" + node.getColumnNumber(), e);
        }
    }
}
